﻿// =============================================
// 文件保护宏：防止该头文件被重复包含（避免编译错误）
// 如果尚未定义 CUSTOM_SHADOWS_INCLUDED，则定义它并继续，否则跳过
// =============================================
#ifndef CUSTOM_SHADOWS_INCLUDED
#define CUSTOM_SHADOWS_INCLUDED

// =============================================
// 引入 Unity 渲染管线核心库中关于阴影采样（Shadow Sampling）的工具函数
// 特别是用于 PCF 滤波的采样点计算，比如 SampleShadow_ComputeSamples_Tent_3x3 等
// 这些函数通常位于 Unity 的 SRP Core 包中，用于生成不同尺寸的采样网格
// =============================================
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Shadow/ShadowSamplingTent.hlsl"

// =============================================
// 定义定向光源的 PCF 滤波采样模式（根据宏定义自动切换）
// 支持 3x3（PCF3）、5x5（PCF5）、7x7（PCF7）滤波，用于实现软阴影效果
// =============================================
#if defined(_DIRECTIONAL_PCF3)
	#define DIRECTIONAL_FILTER_SAMPLES 4      // 实际是 3x3 = 9 个点？注意：这里可能是笔误，通常 3x3 是 9 个采样点
	#define DIRECTIONAL_FILTER_SETUP SampleShadow_ComputeSamples_Tent_3x3
#elif defined(_DIRECTIONAL_PCF5)
	#define DIRECTIONAL_FILTER_SAMPLES 9      // 5x5 = 25 个点？常见为 9（中心 + 四角 + 四边中点）
	#define DIRECTIONAL_FILTER_SETUP SampleShadow_ComputeSamples_Tent_5x5
#elif defined(_DIRECTIONAL_PCF7)
	#define DIRECTIONAL_FILTER_SAMPLES 16     // 7x7 滤波，通常对应 16 或 49 个点，具体看实现
	#define DIRECTIONAL_FILTER_SETUP SampleShadow_ComputeSamples_Tent_7x7
#endif

// =============================================
// 定义最大支持的：带阴影的定向光源数量 和 阴影级联（Cascade）数量
// =============================================
#define MAX_SHADOWED_DIRECTIONAL_LIGHT_COUNT 4
#define MAX_CASCADE_COUNT 4

// =============================================
// 声明一个阴影贴图（Shadow Atlas），类型为 TEXTURE2D_SHADOW
// 这是一个 **特殊类型的纹理**，用于存储多个光源的阴影贴图，并支持硬件比较采样（Shadow Compare）
// 通常是一个拼接的阴影贴图图集（Atlas），包含多个级联 / 光源的阴影数据
// =============================================
TEXTURE2D_SHADOW(_DirectionalShadowAtlas);
// 定义一个采样器，支持阴影比较（sampler_linear_clamp_compare）
#define SHADOW_SAMPLER sampler_linear_clamp_compare
SAMPLER_CMP(SHADOW_SAMPLER);

// =============================================
// 定义一个常量缓冲区（CBUFFER），名称为 _CustomShadows
// 用于在 CPU 端（C# 渲染管线代码）设置并传递以下阴影相关数据到 GPU Shader：
// - 级联数量、级联剔除球、级联数据、阴影矩阵、阴影图集尺寸、阴影距离淡出参数等
// =============================================
CBUFFER_START(_CustomShadows)

	// _CascadeCount：当前使用的阴影级联数量（比如 4 个距离层级）
	int _CascadeCount;

	// _CascadeCullingSpheres[i]：每个级联的剔除球（用于快速判断物体是否在某级联范围内）
	// 是一个 float4，其中 xyz 是球心，w 是球的半径平方（或半径本身，取决于实现）
	float4 _CascadeCullingSpheres[MAX_CASCADE_COUNT];

	// _CascadeData[i]：每个级联的附加数据，比如用于级联混合、Bias 等
	float4 _CascadeData[MAX_CASCADE_COUNT];

	// _DirectionalShadowMatrices：定向光源的阴影变换矩阵数组
	// 每个定向光源 * 每个级联 都有一个矩阵，用于将世界坐标变换到阴影贴图 UV 空间
	// 数组大小为：MAX_SHADOWED_DIRECTIONAL_LIGHT_COUNT * MAX_CASCADE_COUNT
	float4x4 _DirectionalShadowMatrices
		[MAX_SHADOWED_DIRECTIONAL_LIGHT_COUNT * MAX_CASCADE_COUNT];

	// _ShadowAtlasSize：阴影图集的尺寸信息，通常为 float4(xy = 图集尺寸, zw = 可能备用)
	float4 _ShadowAtlasSize;

	// _ShadowDistanceFade：用于控制阴影随距离淡出的参数（比如开始淡出距离、淡出强度等）
	float4 _ShadowDistanceFade;

CBUFFER_END

// =============================================
// 定义一个 ShadowData 结构体，用于存储当前物体所处的阴影级联信息
// 包括：当前级联索引、级联混合因子、以及是否接收阴影的强度
// =============================================
struct ShadowData {
	int cascadeIndex;      // 当前物体所在的阴影级联索引（比如 0、1、2、3）
	float cascadeBlend;    // 级联之间的混合因子（用于平滑过渡相邻级联）
	float strength;        // 阴影强度，范围 [0,1]，0 表示完全无阴影，1 表示完全阴影
};

// =============================================
// 函数：FadedShadowStrength
// 作用：根据距离计算阴影强度的淡出（Fade Out）效果
// 参数：
//   - distance：当前距离（比如物体到摄像机的深度，或到级联边界的距离）
//   - scale：控制淡出速度的系数
//   - fade：淡出强度系数
// 返回值：float，淡出后的阴影强度，范围 [0,1]
// =============================================
float FadedShadowStrength(float distance, float scale, float fade) {
	return saturate((1.0 - distance * scale) * fade);
}

// =============================================
// 函数：GetShadowData
// 作用：根据物体在世界空间中的信息（SurfaceWS），计算它处于哪个阴影级联，
// 以及是否应该接收阴影、级联之间是否需要混合等
// 参数：
//   - surfaceWS：包含世界空间位置、深度、法线等信息的表面结构体
// 返回值：ShadowData，包含 cascadeIndex, cascadeBlend, strength
// =============================================
ShadowData GetShadowData(Surface surfaceWS) {
	ShadowData data;
	data.cascadeBlend = 1.0;             // 默认无混合
	data.strength = FadedShadowStrength(
		surfaceWS.depth, _ShadowDistanceFade.x, _ShadowDistanceFade.y
	); // 根据距离淡出计算阴影强度

	int i;
	for (i = 0; i < _CascadeCount; i++) {
		float4 sphere = _CascadeCullingSpheres[i];
		// 计算物体位置到级联剔除球球心的距离平方
		float distanceSqr = DistanceSquared(surfaceWS.position, sphere.xyz);
		// 如果物体在当前级联球范围内
		if (distanceSqr < sphere.w) {
			// 计算当前级联的淡出因子（通常用于级联边界混合）
			float fade = FadedShadowStrength(
				distanceSqr, _CascadeData[i].x, _ShadowDistanceFade.z
			);
			if (i == _CascadeCount - 1) {
				// 如果是最后一个级联，直接乘到最终强度上
				data.strength *= fade;
			}
			else {
				// 否则记录为级联混合因子（用于与下一级联混合）
				data.cascadeBlend = fade;
			}
			break; // 找到所属级联，退出循环
		}
	}

	// 如果物体不在任何级联范围内，则完全无阴影
	if (i == _CascadeCount) {
		data.strength = 0.0;
	}

	// 可选：支持 Dither 混合（通过像素抖动实现级联过渡更自然）
	#if defined(_CASCADE_BLEND_DITHER)
		else if (data.cascadeBlend < surfaceWS.dither) {
			i += 1; // 切换到下一个级联
		}
	#endif

	// 可选：如果不使用软级联混合，则强制 cascadeBlend = 1.0（即无混合）
	#if !defined(_CASCADE_BLEND_SOFT)
		data.cascadeBlend = 1.0;
	#endif

	data.cascadeIndex = i; // 记录最终所属级联索引
	return data;
}

// =============================================
// 定义一个结构体：DirectionalShadowData
// 用于存储定向光源阴影的附加信息，比如阴影强度、贴图图块索引、法线偏移等
// =============================================
struct DirectionalShadowData {
	float strength;     // 阴影强度
	int tileIndex;      // 在阴影图集中的图块（Tile）索引，用于定位该光源+级联的阴影数据
	float normalBias;   // 法线偏移（用于防止阴影痤疮，即 Shadow Acne）
};

// =============================================
// 函数：SampleDirectionalShadowAtlas
// 作用：从 _DirectionalShadowAtlas（阴影图集）中采样指定位置（positionSTS）的阴影值
// 参数：
//   - positionSTS：阴影空间坐标（Shadow Texture Space），通常是 (x,y) UV + z 深度
// 返回值：float，采样到的阴影强度（0 或 1，或介于之间，取决于 PCF）
// =============================================
float SampleDirectionalShadowAtlas(float3 positionSTS) {
	return SAMPLE_TEXTURE2D_SHADOW(
		_DirectionalShadowAtlas, SHADOW_SAMPLER, positionSTS
	);
}

// =============================================
// 函数：FilterDirectionalShadow
// 作用：对指定阴影位置应用 PCF 滤波（软阴影），根据是否定义了 DIRECTIONAL_FILTER_SETUP
// 来决定是否进行多采样滤波
// 参数：
//   - positionSTS：阴影空间坐标
// 返回值：float，滤波后的阴影值（更柔和）
// =============================================
float FilterDirectionalShadow(float3 positionSTS) {
	#if defined(DIRECTIONAL_FILTER_SETUP)
		// 如果定义了滤波设置，使用多采样点计算 PCF 柔和阴影
		float weights[DIRECTIONAL_FILTER_SAMPLES];
		float2 positions[DIRECTIONAL_FILTER_SAMPLES];
		float4 size = _ShadowAtlasSize.yyxx;
		DIRECTIONAL_FILTER_SETUP(size, positionSTS.xy, weights, positions);
		float shadow = 0;
		for (int i = 0; i < DIRECTIONAL_FILTER_SAMPLES; i++) {
			shadow += weights[i] * SampleDirectionalShadowAtlas(
				float3(positions[i].xy, positionSTS.z)
			);
		}
		return shadow;
	#else
		// 否则进行普通的单点采样（硬阴影）
		return SampleDirectionalShadowAtlas(positionSTS);
	#endif
}

// =============================================
// 函数：GetDirectionalShadowAttenuation
// 作用：计算当前物体在定向光源下的 **阴影衰减（Attenuation）**
// 即：最终该像素是否会受到阴影影响，以及影响有多强（0 ~ 1）
// 参数：
//   - directional：定向光源的阴影数据（强度、图块索引、法线偏移）
//   - global：全局阴影数据（级联索引、混合、强度等）
//   - surfaceWS：世界空间表面信息（位置、法线、深度等）
// 返回值：float，阴影衰减值，1.0 表示完全无阴影，0.0 表示完全阴影
// =============================================
float GetDirectionalShadowAttenuation(
	DirectionalShadowData directional, ShadowData global, Surface surfaceWS
) {
	#if !defined(_RECEIVE_SHADOWS)
		// 如果不接收阴影（比如通过 Shader Feature 控制），直接返回 1.0（无阴影）
		return 1.0;
	#endif

	if (directional.strength <= 0.0) {
		// 如果该光源的阴影强度为 0，则无阴影
		return 1.0;
	}

	// 计算法线偏移：防止阴影痤疮（Shadow Acne）
	// 根据表面法线和设定的 Bias 值，偏移阴影投射位置
	float3 normalBias = surfaceWS.normal *
		(directional.normalBias * _CascadeData[global.cascadeIndex].y);

	// 将世界坐标变换到阴影贴图空间（通过阴影矩阵）
	float3 positionSTS = mul(
		_DirectionalShadowMatrices[directional.tileIndex],
		float4(surfaceWS.position + normalBias, 1.0)
	).xyz;

	// 采样当前级联的阴影值
	float shadow = FilterDirectionalShadow(positionSTS);

	// 如果不是最后一个级联，且存在级联混合，则需要对相邻级联也采样并混合
	if (global.cascadeBlend < 1.0) {
		float3 normalBiasNext = surfaceWS.normal *
			(directional.normalBias * _CascadeData[global.cascadeIndex + 1].y);
		float3 positionSTSNext = mul(
			_DirectionalShadowMatrices[directional.tileIndex + 1],
			float4(surfaceWS.position + normalBiasNext, 1.0)
		).xyz;
		float shadowNext = FilterDirectionalShadow(positionSTSNext);
		// 级联混合：根据 blend 因子插值两个级联的阴影值
		shadow = lerp(FilterDirectionalShadow(positionSTSNext), shadow, global.cascadeBlend);
	}

	// 最终返回：根据阴影强度（directional.strength）插值出阴影衰减结果
	// 即：lerp(完全无阴影, 采样到的阴影值, 阴影强度)
	return lerp(1.0, shadow, directional.strength);
}

// =============================================
// 文件保护宏结束
// =============================================
#endif